var SerialHandle:longint;



procedure LogEXTS(S:string);       // log with timestamp and control characters translated
var H24, M60, S60, ms:word;
                    I:integer;
begin

    DecodeTime(Time, H24, M60, S60, ms);
    writeln(Format('%.2dh %.2dm %.2ds %.4dms   %d bytes',[H24, M60, S60, MS, length(S)]));

    for I:=1 to length(S) do
    if S[I]<#32 then case S[I] of #07:write('<BEL>');        // bell
                                  #08:write('<BS>');         // backspace
                                  #09:write('<TAB>');        // tab
                                  #10:write('<LF>');         // linefeed
                                  #13:write('<CR>');         // carriage return
                                  #27:write('<ESC>')         // escape
                               else   write('<', IntToHex(ord(S[I]), 2), '>')
                     end  { of case }
                else write(S[I]);
    writeln;
    writeln
end;


procedure SetupCommPort(CommPortName:string; BaudRate, BitCount, Parity, StopBits: integer);
var tios:termios;                                  //  5,6,7,8   0,1,2     1,2
       I:integer;                                  //            0=none
begin                                              //            1=odd
  CONNECTED:=0;                                    //            2=even
  try fpClose(SerialHandle) except end;

  try I:=fpOpen(CommPortName, O_RDWR or O_NOCTTY) except I:=-1 end;
  if SerialHandle<0 then
  begin
    try fpClose(SerialHandle) except end;
    ShowMessage(#13+pL+'Failed to open '+CommPortName+pR+#13);
    exit
  end;

  SerialHandle:=I;

  FillChar(tios, SizeOf(tios), #0);

  case BaudRate of 50:tios.c_cflag:=B50;
                   75:tios.c_cflag:=B75;
                  110:tios.c_cflag:=B110;
                  134:tios.c_cflag:=B134;
                  150:tios.c_cflag:=B150;
                  200:tios.c_cflag:=B200;
                  300:tios.c_cflag:=B300;
                  600:tios.c_cflag:=B600;
                 1200:tios.c_cflag:=B1200;
                 1800:tios.c_cflag:=B1800;
                 2400:tios.c_cflag:=B2400;
                 4800:tios.c_cflag:=B4800;
                 9600:tios.c_cflag:=B9600;
                19200:tios.c_cflag:=B19200;
                38400:tios.c_cflag:=B38400;
                57600:tios.c_cflag:=B57600;
               115200:tios.c_cflag:=B115200;
               230400:tios.c_cflag:=B230400;
{$ifndef BSD}
               460800:tios.c_cflag:=B460800;
{$endif}
                 else tios.c_cflag:=B38400;
  end;  { of case }

{$ifndef SOLARIS}
  tios.c_ispeed:=tios.c_cflag;
  tios.c_ospeed:=tios.c_cflag;
{$endif}

  tios.c_cflag:=tios.c_cflag or CREAD or CLOCAL;

  case BitCount of 5:tios.c_cflag:=tios.c_cflag or CS5;
                   6:tios.c_cflag:=tios.c_cflag or CS6;
                   7:tios.c_cflag:=tios.c_cflag or CS7;
                   8:tios.c_cflag:=tios.c_cflag or CS8
                else tios.c_cflag:=tios.c_cflag or CS8
  end;  { of case }

  case Parity of 0:;
                 1:tios.c_cflag:=tios.c_cflag or PARENB or PARODD;
                 2:tios.c_cflag:=tios.c_cflag or PARENB
  end;  { of case }

  case StopBits of 1:;
                   2:tios.c_cflag:=tios.c_cflag or CSTOPB
  end;  { of case }

//  tios.c_cflag := tios.c_cflag or CRTSCTS;                  // enable RTS/CTS flow control

  try
    tcFlush(SerialHandle, TCIOFLUSH)
  except
    try fpClose(SerialHandle) except end;
    ShowMessage(#13+pL+'Failed to flush '+CommPortName+pR+#13);
    exit
  end;

  try
    tcSetAttr(SerialHandle, TCSANOW, tios)
  except
    try fpClose(SerialHandle) except end;
    ShowMessage(#13+pL+'Failed to configure '+CommPortName+pR+#13);
    exit
  end;

  TS1:=GetTickCount64;
  TS2:=TS1;
  TS3:=TS2;
  TS4:=TS3;

  CONNECTED:=2;
  CommName:=CommPortName;
  CommRate:=BaudRate;
  CONNECTED:=2
end;


procedure SetupCommPortS(CommPortName, Config:string);
begin
  ShowMessage(#13+pL+CommPortName+' : '+Config+pR+#13+
                  pl+'serial setup with config string not yet implemented'+pR+#13)
end;


function ReadComm(var Buffer:str255):boolean;
//const CTS_flag:integer=TIOCM_CTS;
//      DSR_flag:integer=TIOCM_DSR;
const lock:boolean=false;
var I:integer;
begin
  if lock or (CONNECTED<>2) then result:=false else
  begin
    lock:=true;

    try I:=fpRead(SerialHandle, Buffer[1], 250) except I:=-1 end;
//  try FpIOCtl(SerialHandle, TIOCMGET, @CTS_flag) except end;                              // failed attempt to check that USB bridge has not been unpluggeed

    if I<0 then begin
                  CONNECTED:=0;
                  try fpClose(SerialHandle) except end;
                  ShowMessage(#13+pL+'Error reading data from comm port'+pR+#13);
                  Result:=false
                end else
    if I=0 then begin
                  Result:=false
                end
           else begin
                  Buffer[0]:=char(I);
                  Result:=true;
                  TS1:=GetTickCount64;
                  if DEBUGMODE=2 then LogEXTS(Buffer)
                end;
    lock:=false
  end
end;


function WriteComm(Buffer:str255):boolean;
const lock:boolean=false;
var I:integer;
begin
  if lock or (CONNECTED<>2) then result:=false else
  begin
    lock:=true;

    try I:=fpWrite(SerialHandle, Buffer[1], length(Buffer)) except I:=-1 end;

    if I<0 then begin
                  CONNECTED:=0;
                  try fpClose(SerialHandle) except end;
                  ShowMessage(#13+pL+'Error writing data to comm port'+pR+#13);
                  Result:=false
                end
           else begin
                  Result:=true;
                  TS2:=GetTickCount64
                end;
    lock:=false
  end
end;


procedure CloseCommPort;
begin
  CONNECTED:=0;
  try fpClose(SerialHandle) except end
end;


